using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Graphics.PackedVector;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;
using System.Diagnostics;


namespace WPCoco.Graphics
{
    public class GraphicPrimitives
    {
        static GraphicPrimitives()
        {
            basicEffect = new BasicEffect(Screen.CurrentGraphicsDevice);
            basicEffect.VertexColorEnabled = true;
            basicEffect.TextureEnabled = false;
            basicEffect.Projection = Matrix.CreateOrthographicOffCenter(1, Screen.Width,     // left, right
                                                                        Screen.Height, 1,    // bottom, top
                                                                        0, 1);
            bufLine = new VertexPositionColor[6];

        }

        static Matrix worldMatrix = Matrix.Identity;

        public static Matrix WorldMatrix
        {
            get { return worldMatrix; }
            set { worldMatrix = value; }
        }

        static BasicEffect basicEffect;
        static VertexPositionColor[] bufLine;

        static void GrowBuffer(int amount) 
        {
            Debug.WriteLine(">>> Growing primitives buffer");
            VertexPositionColor[] temp = new VertexPositionColor[bufLine.Length * 2 + amount];
            bufLine.CopyTo(temp, 0);
            bufLine = temp;
        }

        public static void DrawLine(Vector2 start, Vector2 end, Color color) 
        {
            bufLine[0].Color = color; bufLine[0].Position = new Vector3(start.X, start.Y, 0);
            bufLine[1].Color = color; bufLine[1].Position = new Vector3(end.X, end.Y, 0);

            basicEffect.World = worldMatrix;
            basicEffect.CurrentTechnique.Passes[0].Apply();

            Screen.CurrentGraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineList, bufLine, 0, 1);
        }

        public static void DrawRectangle(Vector2 start, Vector2 end, Color color)
        {
            bufLine[0].Color = color; bufLine[0].Position = new Vector3(start.X, start.Y, 0);
            bufLine[1].Color = color; bufLine[1].Position = new Vector3(start.X, end.Y, 0);
            bufLine[2].Color = color; bufLine[2].Position = new Vector3(end.X, end.Y, 0);
            bufLine[3].Color = color; bufLine[3].Position = new Vector3(end.X, start.Y, 0);
            bufLine[4].Color = color; bufLine[4].Position = new Vector3(start.X, start.Y, 0);

            basicEffect.World = worldMatrix;
            basicEffect.CurrentTechnique.Passes[0].Apply();

            Screen.CurrentGraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineStrip, bufLine, 0, 4);
        }

        public static void DrawCircle(Vector2 center, float radius, float angle, int segments, Color color)
        {
            float coef = 2.0f * (float)(Math.PI) / segments;

            int dif = bufLine.Length - 2 * (segments + 2);
            if (dif < 0) GrowBuffer(-dif);

            VertexPositionColor[] vertices = bufLine; 

            for (int i = 0; i <= segments; i++)
            {
                float rads = i * coef;
                float j = radius * (float)Math.Cos(rads + angle) + center.X;
                float k = radius * (float)Math.Sin(rads + angle) + center.Y;

                vertices[i].Position = new Vector3(j, k, 0);
                vertices[i].Color = color;
            }

            basicEffect.World = worldMatrix;
            basicEffect.CurrentTechnique.Passes[0].Apply();

            Screen.CurrentGraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.LineStrip, bufLine, 0, segments);
        }
         
        /*
        public static void FillRectangle(Vector2 start, Vector2 end, Color color)
        {
            bufLine[0].Color = color; bufLine[1].Position = new Vector3(start.X, start.Y, 0);
            bufLine[1].Color = color; bufLine[0].Position = new Vector3(start.X, end.Y, 0);
            bufLine[2].Color = color; bufLine[2].Position = new Vector3(end.X, end.Y, 0);
            bufLine[3].Color = color; bufLine[4].Position = new Vector3(start.X, start.Y, 0);
            bufLine[4].Color = color; bufLine[3].Position = new Vector3(end.X, start.Y, 0);

            basicEffect.World = worldMatrix;
            basicEffect.CurrentTechnique.Passes[0].Apply();

            Screen.CurrentGraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleStrip, bufLine, 0, 2);
        }
        */

        #region Fill Rectangles

        public static void FillRectangle(Vector2 start, Vector2 end, Color color)
        {
            bufLine[0].Color = color; bufLine[0].Position = new Vector3(start.X, start.Y, 0);
            bufLine[1].Color = color; bufLine[1].Position = new Vector3(end.X, end.Y, 0);
            bufLine[2].Color = color; bufLine[2].Position = new Vector3(start.X, end.Y, 0);
            bufLine[3].Color = color; bufLine[3].Position = new Vector3(start.X, start.Y, 0);
            bufLine[4].Color = color; bufLine[4].Position = new Vector3(end.X, start.Y, 0);
            bufLine[5].Color = color; bufLine[5].Position = new Vector3(end.X, end.Y, 0);

            basicEffect.World = worldMatrix;
            basicEffect.CurrentTechnique.Passes[0].Apply();

            Screen.CurrentGraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, bufLine, 0, 2);
        }

        public static void FillRectangleHorizontalGradient(Vector2 start, Vector2 end, Color colorFirst, Color colorSecond)
        {
            bufLine[0].Color = colorFirst; bufLine[0].Position = new Vector3(start.X, start.Y, 0);
            bufLine[1].Color = colorSecond; bufLine[1].Position = new Vector3(end.X, end.Y, 0);
            bufLine[2].Color = colorFirst; bufLine[2].Position = new Vector3(start.X, end.Y, 0);
            bufLine[3].Color = colorFirst; bufLine[3].Position = new Vector3(start.X, start.Y, 0);
            bufLine[4].Color = colorSecond; bufLine[4].Position = new Vector3(end.X, start.Y, 0);
            bufLine[5].Color = colorSecond; bufLine[5].Position = new Vector3(end.X, end.Y, 0);

            basicEffect.World = worldMatrix;
            basicEffect.CurrentTechnique.Passes[0].Apply();

            Screen.CurrentGraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, bufLine, 0, 2);
        }

        public static void FillRectangleVerticalGradient(Vector2 start, Vector2 end, Color colorFirst, Color colorSecond)
        {
            bufLine[0].Color = colorFirst; bufLine[0].Position = new Vector3(start.X, start.Y, 0);
            bufLine[1].Color = colorSecond; bufLine[1].Position = new Vector3(end.X, end.Y, 0);
            bufLine[2].Color = colorSecond; bufLine[2].Position = new Vector3(start.X, end.Y, 0);
            bufLine[3].Color = colorFirst; bufLine[3].Position = new Vector3(start.X, start.Y, 0);
            bufLine[4].Color = colorFirst; bufLine[4].Position = new Vector3(end.X, start.Y, 0);
            bufLine[5].Color = colorSecond; bufLine[5].Position = new Vector3(end.X, end.Y, 0);

            basicEffect.World = worldMatrix;
            basicEffect.CurrentTechnique.Passes[0].Apply();

            Screen.CurrentGraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, bufLine, 0, 2);
        }

        public static void FillRectangleCornerColored(Vector2 start, Vector2 end, Color[] colors)
        {
            bufLine[0].Color = colors[0]; bufLine[0].Position = new Vector3(start.X, start.Y, 0);
            bufLine[1].Color = colors[3]; bufLine[1].Position = new Vector3(end.X, end.Y, 0);
            bufLine[2].Color = colors[2]; bufLine[2].Position = new Vector3(start.X, end.Y, 0);
            bufLine[3].Color = colors[0]; bufLine[3].Position = new Vector3(start.X, start.Y, 0);
            bufLine[4].Color = colors[1]; bufLine[4].Position = new Vector3(end.X, start.Y, 0);
            bufLine[5].Color = colors[3]; bufLine[5].Position = new Vector3(end.X, end.Y, 0);

            basicEffect.World = worldMatrix;
            basicEffect.CurrentTechnique.Passes[0].Apply();

            Screen.CurrentGraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, bufLine, 0, 2);
        }

        #endregion

        #region Add Fill Rectangles

        static int triangles = 0;

        public static void AddFillRectangle(Vector2 start, Vector2 end, Color color)
        {
            int offset = triangles * 3;
            if (bufLine.Length < (triangles + 2) * 3) GrowBuffer(6);
            bufLine[offset + 0].Color = color; bufLine[offset + 0].Position = new Vector3(start.X, start.Y, 0);
            bufLine[offset + 1].Color = color; bufLine[offset + 1].Position = new Vector3(end.X, end.Y, 0);
            bufLine[offset + 2].Color = color; bufLine[offset + 2].Position = new Vector3(start.X, end.Y, 0);
            bufLine[offset + 3].Color = color; bufLine[offset + 3].Position = new Vector3(start.X, start.Y, 0);
            bufLine[offset + 4].Color = color; bufLine[offset + 4].Position = new Vector3(end.X, start.Y, 0);
            bufLine[offset + 5].Color = color; bufLine[offset + 5].Position = new Vector3(end.X, end.Y, 0);
            triangles += 2;
        }

        public static void AddFillRectangleHorizontalGradient(Vector2 start, Vector2 end, Color colorFirst, Color colorSecond)
        {
            int offset = triangles * 3;
            if (bufLine.Length < (triangles + 2) * 3) GrowBuffer(6);
            bufLine[offset + 0].Color = colorFirst; bufLine[offset + 0].Position = new Vector3(start.X, start.Y, 0);
            bufLine[offset + 1].Color = colorSecond; bufLine[offset + 1].Position = new Vector3(end.X, end.Y, 0);
            bufLine[offset + 2].Color = colorFirst; bufLine[offset + 2].Position = new Vector3(start.X, end.Y, 0);
            bufLine[offset + 3].Color = colorFirst; bufLine[offset + 3].Position = new Vector3(start.X, start.Y, 0);
            bufLine[offset + 4].Color = colorSecond; bufLine[offset + 4].Position = new Vector3(end.X, start.Y, 0);
            bufLine[offset + 5].Color = colorSecond; bufLine[offset + 5].Position = new Vector3(end.X, end.Y, 0);
            triangles += 2;
        }

        public static void AddFillRectangleVerticalGradient(Vector2 start, Vector2 end, Color colorFirst, Color colorSecond)
        {
            int offset = triangles * 3;
            if (bufLine.Length < (triangles + 2) * 3) GrowBuffer(6);
            bufLine[offset + 0].Color = colorFirst; bufLine[offset + 0].Position = new Vector3(start.X, start.Y, 0);
            bufLine[offset + 1].Color = colorSecond; bufLine[offset + 1].Position = new Vector3(end.X, end.Y, 0);
            bufLine[offset + 2].Color = colorSecond; bufLine[offset + 2].Position = new Vector3(start.X, end.Y, 0);
            bufLine[offset + 3].Color = colorFirst; bufLine[offset + 3].Position = new Vector3(start.X, start.Y, 0);
            bufLine[offset + 4].Color = colorFirst; bufLine[offset + 4].Position = new Vector3(end.X, start.Y, 0);
            bufLine[offset + 5].Color = colorSecond; bufLine[offset + 5].Position = new Vector3(end.X, end.Y, 0);
            triangles += 2;
        }

        public static void AddFillRectangleCornerColored(Vector2 start, Vector2 end, Color[] colors)
        {
            int offset = triangles * 3;
            if (bufLine.Length < (triangles + 2) * 3) GrowBuffer(6);
            bufLine[offset + 0].Color = colors[0]; bufLine[0].Position = new Vector3(start.X, start.Y, 0);
            bufLine[offset + 1].Color = colors[3]; bufLine[1].Position = new Vector3(end.X, end.Y, 0);
            bufLine[offset + 2].Color = colors[2]; bufLine[2].Position = new Vector3(start.X, end.Y, 0);
            bufLine[offset + 3].Color = colors[0]; bufLine[3].Position = new Vector3(start.X, start.Y, 0);
            bufLine[offset + 4].Color = colors[1]; bufLine[4].Position = new Vector3(end.X, start.Y, 0);
            bufLine[offset + 5].Color = colors[3]; bufLine[5].Position = new Vector3(end.X, end.Y, 0);
            triangles += 2;
        }

        public static void AddFillCircle(Vector2 center, float radius, float angle, int segments, Color color)
        {
            int offset = triangles * 3;

            float coef = 2.0f * (float)(Math.PI) / segments;

            if (bufLine.Length < (triangles + segments) * 3) GrowBuffer(segments * 3);

            VertexPositionColor[] vertices = bufLine;

            float j = radius * (float)Math.Cos(-coef + angle) + center.X, j0 = j;
            float k = radius * (float)Math.Sin(-coef + angle) + center.Y, k0 = k;

            for (int i = 0; i < segments; i++)
            {
                float rads = i * coef;
                j = radius * (float)Math.Cos(rads + angle) + center.X;
                k = radius * (float)Math.Sin(rads + angle) + center.Y;

                vertices[offset + 3 * i + 0].Position = new Vector3(center, 0);
                vertices[offset + 3 * i + 0].Color = color;

                if (i != 0)
                    vertices[offset + 3 * i + 1].Position = vertices[offset + 3 * i - 1].Position;
                else
                    vertices[offset + 3 * i + 1].Position = new Vector3(j0, k0, 0);
                vertices[offset + 3 * i + 1].Color = color;

                vertices[offset + 3 * i + 2].Position = new Vector3(j, k, 0);
                vertices[offset + 3 * i + 2].Color = color;
            }
            triangles += segments;
        }

        public static void ResetFill() 
        {
            triangles = 0;
        } 

        public static void FlushFill() 
        {
            if (triangles == 0) return;

            basicEffect.World = worldMatrix;
            basicEffect.CurrentTechnique.Passes[0].Apply();

            Screen.CurrentGraphicsDevice.BlendState = BlendState.AlphaBlend;
            Screen.CurrentGraphicsDevice.DrawUserPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, bufLine, 0, triangles);
            triangles = 0;
        }

        #endregion

    }
}
